initForms();

function initForms()
{
    var forms = document.getElementsByTagName( "form" );
    for ( var i in forms )
    {
        var form = forms[i];
        if ( form.id )
        {
            for ( var i in form.elements )
            {
                var element = form.elements[i];
                if ( element.id && element.tagName.toLowerCase() != "button" )
                {
                    goog.dom.classes.add( element, "waiting" );
                }
            }

            sendGetModelRequest( form );
        }
    }
}

goog.events.listen( document.body, goog.events.EventType.CLICK, function( e )
{
    var el = (e).target;

    if ( el.tagName.toLowerCase() == "button" && el.type == "submit" && el.getAttribute( "ajax" ) )
    {
        goog.dom.classes.add( el, "waiting" );
        var form = el.form;
        if ( !form )
        {
            return;
        }

        var model = new Object();
        for ( var i in form.elements )
        {
            var elm = form.elements[i];

            if ( !elm.id )
            {
                continue;
            }

            // input
            var field = null;

            if ( elm.tagName.toLowerCase() == "input" )
            {
                if ( elm.type == "checkbox" )
                {
                    field = elm.checked ? "true" : "false";
                }
                else
                {
                    field = elm.value == "" ? null : elm.value;
                }
            }

            // textarea
            if ( elm.tagName.toLowerCase() == "textarea" )
            {
                field = elm.value == "" ? null : elm.value;
            }

            // select
            // TODO: implement listbox
            if ( elm.tagName.toLowerCase() == "select" )
            {
                var val = elm.options[elm.selectedIndex].value;
                field = val == "" ? null : val;
            }

            if ( elm.tagName.toLowerCase() != "button" )
            {
                model[elm.id] = field;
            }
        }

        var data = goog.Uri.QueryData.createFromMap( new goog.structs.Map( {
            "__model_id": form.id,
            "__model": form.getAttribute( "model" ),
            "__json" : goog.json.serialize( model ),
            "__ajax_handler": form.getAttribute( "ph" )
        } ) );

        data.extend( new goog.Uri( document.location.href ).getQueryData() );

        sendPostModelRequest( form.action, data, form.getAttribute( "method" ), el );
        e.preventDefault();
    }
} );

function sendGetModelRequest( form )
{
    var xhr = new goog.net.XhrIo();
    goog.events.listen( xhr, goog.net.EventType.SUCCESS, function( e )
    {
        // fill form with model values
        try
        {
            var response = e.target.getResponseJson();
            if ( response["messages"] )
            {
                handleMessages( response );
            }

            var model = response["model"];
            for ( var i in form.elements )
            {
                var elm = form.elements[i];
                if ( !elm.id )
                {
                    continue;
                }

                var value = model[elm.id];
                var tag = elm.tagName.toLowerCase();
                if ( (tag == "input" && (elm.type == "text" || elm.type == "password")) || tag == "textarea" )
                {
                    elm.value = value;
                }

                // TODO: implement list box
                if ( tag == "select" )
                {
                    for ( var j in elm.options )
                    {
                        if ( elm.options[j].value == value )
                        {
                            elm.options[j].selected = true;
                        }
                    }
                }

                goog.dom.classes.remove( elm, "waiting" );
            }
        }
        catch ( err )
        {
            if ( console )
            {
                console.error( err );
            }
        }
    } );

    // set ajax header
    var headers = new goog.structs.Map();
    headers.set( "X-Requested-With", "XMLHttpRequest" );

    var href = document.location.href;
    var data = new goog.Uri.QueryData( href.indexOf( "?" ) != -1 ? href.substring( href.indexOf( "?" ) + 1 ) : null );
    data.add( "__model_id", form.id );
    data.add( "__ajax_handler", form.getAttribute( "gh" ) );

    // send request
    xhr.send( form.action, "POST", data.toString(), headers );
}

function sendPostModelRequest( uri, data, method, button )
{
    var xhr = new goog.net.XhrIo();
    goog.events.listen( xhr, goog.net.EventType.SUCCESS, function( e )
    {
        try
        {
            var response = e.target.getResponseJson();
            if ( response["hasErrors"] )
            {
                handleMessages( response );
            } else if ( response["responseType"] == "REDIRECT" )
            {
                document.location.href = response["value"];
            }
        }
        catch ( err )
        {
            if ( console )
            {
                console.error( err );
            }
        }
        goog.dom.classes.remove( button, "waiting" );
    } );

    // set ajax header
    var headers = new goog.structs.Map();
    headers.set( "X-Requested-With", "XMLHttpRequest" );

    // send request
    xhr.send( uri, method, data.toString(), headers );
}

/**
 * Handle json messages
 *
 * @param response
 */
function handleMessages( response )
{
    var messages = response["messages"];

    // remove previous error field messages
    var items = goog.dom.getElementsByClass( "message-box-item" );
    for ( var i in items )
    {
        goog.dom.removeNode( items[i] );
    }

    // remove inputs marked as error
    items = goog.dom.getElementsByClass( "error" );
    for ( var i in items )
    {
        goog.dom.classes.remove( items[i], "error" );
    }

    // remove previous error feedback messages
    items = goog.dom.getElementsByClass( "feedback-box" );
    for ( var i in items )
    {
        goog.dom.removeNode( items[i] );
    }

    // create new field and feedback messages
    for ( var i in messages )
    {
        var message = messages[i];
        var level = message["level"].toLowerCase();

        // field messages
        if ( message["type"] == "FIELD" )
        {
            var field = document.getElementById( message["id"] );
            if ( field )
            {
                goog.dom.classes.add( field, level );

                // create message wrapper
                var wrapperId = "message-" + message["id"];
                var messageDivWrapper = document.getElementById( wrapperId );
                if ( !messageDivWrapper )
                {
                    messageDivWrapper = createDiv( "" );
                    messageDivWrapper.className = "message-box";
                    messageDivWrapper.setAttribute( "id", "message-" + message["id"] );

                    // ensure that we will append error at the end (component can be first, before label)
                    var lastElm = field;
                    while ( goog.dom.getNextElementSibling( lastElm ) )
                    {
                        lastElm = goog.dom.getNextElementSibling( lastElm );
                    }
                    goog.dom.insertSiblingAfter( messageDivWrapper, lastElm );
                }

                // create item
                var messageDiv = createDiv( message["message"] );
                messageDiv.className = "message-box-item " + "message-box-item-" + level;
                goog.dom.appendChild( messageDivWrapper, messageDiv );
            }
        }
        // feedback messages
        else if ( message["type"] == "FEEDBACK" )
        {
            var feedbackBox = createDiv( "" );
            feedbackBox.className = "feedback-box " + "feedback-box-" + level;
            goog.dom.insertChildAt( document.getElementsByTagName( "section" )[0], feedbackBox, 0 );

            goog.dom.appendChild( feedbackBox, createDiv( message["message"] ) );
        }
    }
}

function createDiv( content )
{
    return goog.dom.createDom( "div", {}, content );
}

